/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package br.ufrj.dcc.pushpeer.protocol;

import java.util.ArrayList;
import java.util.List;

import peersim.core.CommonState;
import peersim.core.Node;

/**
 * Implements the "Known peers' rarest files" strategy.
 */
public class PushPeerKnownRarest extends PushPeer {

	/**
	 * Size of the rarest files list.
	 * TODO_L: Define the size of the rarest file list (phi).
	 */
	public static final int RAREST_LIST_SIZE = 5;
	
	/**
	 * This number gives a weight to the old cache request, to avoid too much cache swapping.
	 */
	public static final int OLD_CACHE_INERTIA = 8;
	
	/**
	 * Holds the last file this peer (as a Cache Bank) requested to it's managed peer.
	 */
	protected int lastRequestedCache = -1;
	
	/**
	 * Holds the number of times the last file appeared in the rarest lists. Files with a high count should be kept for a longer time.
	 */
	protected int lastRequestCount = -1;
	
	public PushPeerKnownRarest(String prefix) {
		super(prefix);
	}

	/**
	 * This strategy picks the most appearing file on the known peers' rarest files lists.
	 * @param pid PushPeer's protocol id.
	 * @return the file to be requested for cache.
	 */
	@Override
	protected int replicationStrategy(int pid) {
		
		int rarestFile = -1;
		
		if(managedPeers != null) {
			//First, fetch the rarest files from the known peers.
			int numFiles = fileNumPiecesCompleted.size();
			int totalQueriedFiles = 0;
			
			List<Integer> rarest = new ArrayList<Integer>(numFiles+1);
			//Adds one more, because index 0 is not used.
			for(int i=1; i <= numFiles+1; i++)
				rarest.add(0);
			
			for(Node n : getKnownPeers()) {
				List<Integer> rarestPeerFiles = ((PushPeer)n.getProtocol(pid)).getRarestFile(RAREST_LIST_SIZE);
				if(!rarestPeerFiles.isEmpty()) {//Otherwise the peer isn't interested in anything.
					totalQueriedFiles += rarestPeerFiles.size();
					for(Integer i : rarestPeerFiles)
						rarest.set(i, rarest.get(i)+1);
				}
			}
			if(totalQueriedFiles == 0)
				//None of the known peers want a file (should only happen near an equilibrium state).
				return rarestFile;
			
			double prob = CommonState.r.nextDouble();
			int current = 0;
			int count = -1;
			for(int i=1; i <= numFiles; i++) {
				count = rarest.get(i);
				current += count;
				if(prob < (double)current/totalQueriedFiles) {
					rarestFile = i;
					break;
				}
			}
			
			prob = CommonState.r.nextDouble();
			double threshold;
			if(lastRequestedCache != -1) {
				threshold = (lastRequestCount*OLD_CACHE_INERTIA)/(lastRequestCount*OLD_CACHE_INERTIA + count);
				if(prob < threshold) {
					rarestFile = lastRequestedCache;
				}
				else {
					lastRequestCount = count;
					lastRequestedCache = rarestFile;
				}
			}
			else {
				lastRequestCount = count;
				lastRequestedCache = rarestFile;
			}
			
		}
		
		// -1 here means there is no managed peer.
		return rarestFile;
	}

}
